namespace NFTRedit
{
    using System;
    using System.Collections.Generic;
    using System.IO;

    public class CMAPChunk
    {
        private static readonly uint headerLength = 20;
        private static readonly uint MAGIC = 0x434d4150;
        private ushort mEndCode;
        private Dictionary<ushort, ushort> mMappings;
        private uint mNextOffset;
        private uint mSize;
        private ushort mStartCode;
        private ushort mType;
        private ushort mType0BaseIndex;
        private ushort[] mType1Indices;
        private ushort[][] mType2Mappings;
        private ushort mUnknown;

        public CMAPChunk(BinaryReader reader)
        {
            this.Load(reader);
        }

        private void Load(BinaryReader reader)
        {
            this.ReadHeader(reader);
            this.ReadBody(reader);
        }

        private void ReadBody(BinaryReader reader)
        {
            ushort num;
            ushort mStartCode;
            ushort num3;
            this.mMappings = new Dictionary<ushort, ushort>();
            switch (this.mType)
            {
                case 0:
                    num = (ushort) ((this.mEndCode - this.mStartCode) + 1);
                    mStartCode = this.mStartCode;
                    num3 = reader.ReadUInt16();
                    this.mType0BaseIndex = num3;
                    for (int i = 0; i < num; i++)
                    {
                        this.mMappings.Add(mStartCode, num3);
                        mStartCode = (ushort) (mStartCode + 1);
                        num3 = (ushort) (num3 + 1);
                    }
                    break;

                case 1:
                    num = (ushort) ((this.mEndCode - this.mStartCode) + 1);
                    mStartCode = this.mStartCode;
                    this.mType1Indices = new ushort[num];
                    for (int j = 0; j < num; j++)
                    {
                        num3 = reader.ReadUInt16();
                        this.mType1Indices[j] = num3;
                        this.mMappings.Add(mStartCode, num3);
                        mStartCode = (ushort) (mStartCode + 1);
                    }
                    break;

                case 2:
                    num = reader.ReadUInt16();
                    this.mType2Mappings = new ushort[num][];
                    for (int k = 0; k < num; k++)
                    {
                        mStartCode = reader.ReadUInt16();
                        num3 = reader.ReadUInt16();
                        this.mType2Mappings[k] = new ushort[] { mStartCode, num3 };
                        this.mMappings.Add(mStartCode, num3);
                    }
                    break;
            }
            if ((reader.BaseStream.Position % 4L) != 0L)
            {
                reader.ReadUInt16();
            }
        }

        private void ReadHeader(BinaryReader reader)
        {
            if (reader.ReadUInt32() != MAGIC)
            {
                throw new FormatException("Chunk is not CMAP format.");
            }
            this.mSize = reader.ReadUInt32();
            this.mStartCode = reader.ReadUInt16();
            this.mEndCode = reader.ReadUInt16();
            this.mType = reader.ReadUInt16();
            this.mUnknown = reader.ReadUInt16();
            this.mNextOffset = reader.ReadUInt32();
        }

        public void RecalculateSize()
        {
            int headerLength = (int) CMAPChunk.headerLength;
            switch (this.mType)
            {
                case 0:
                    headerLength += 2;
                    break;

                case 1:
                    headerLength += 2 * this.mType1Indices.Length;
                    break;

                case 2:
                    headerLength += 4 * this.mType2Mappings.Length;
                    break;
            }
            while ((headerLength % 4) != 0)
            {
                headerLength += 2;
            }
        }

        public void Save(BinaryWriter writer, bool isLast)
        {
            writer.Flush();
            int position = (int) writer.BaseStream.Position;
            if (isLast)
            {
                this.mNextOffset = 0;
            }
            else
            {
                this.NextCmapOffset = position + ((int) this.mSize);
            }
            this.WriteHeader(writer);
            this.WriteBody(writer);
        }

        private void WriteBody(BinaryWriter writer)
        {
            switch (this.mType)
            {
                case 0:
                    writer.Write(this.mType0BaseIndex);
                    break;

                case 1:
                    for (int i = 0; i < this.mType1Indices.Length; i++)
                    {
                        writer.Write(this.mType1Indices[i]);
                    }
                    break;

                case 2:
                    writer.Write((ushort) this.mType2Mappings.Length);
                    for (int j = 0; j < this.mType2Mappings.Length; j++)
                    {
                        writer.Write(this.mType2Mappings[j][0]);
                        writer.Write(this.mType2Mappings[j][1]);
                    }
                    break;
            }
            writer.Flush();
            if ((writer.BaseStream.Position % 4L) != 0L)
            {
                writer.Write((ushort) 0);
            }
        }

        private void WriteHeader(BinaryWriter writer)
        {
            writer.Write(MAGIC);
            writer.Write(this.mSize);
            writer.Write(this.mStartCode);
            writer.Write(this.mEndCode);
            writer.Write(this.mType);
            writer.Write(this.mUnknown);
            writer.Write(this.mNextOffset);
        }

        public int Length
        {
            get
            {
                return (int) this.mSize;
            }
        }

        public Dictionary<ushort, ushort> Mappings
        {
            get
            {
                return this.mMappings;
            }
        }

        public int NextCmapOffset
        {
            get
            {
                if (this.mNextOffset != 0)
                {
                    return (((int) this.mNextOffset) - 8);
                }
                return 0;
            }
            set
            {
                this.mNextOffset = (uint) (value + 8);
            }
        }
    }
}

